package gwtI18nEditor.filetools;

import gwtI18nEditor.datamodel.Directory;
import gwtI18nEditor.datamodel.MessageFile;
import gwtI18nEditor.datamodel.MessageType;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JavaFileParser {

	// TODO: Verify patterns! 
	private static final String LINE_COMMENT         = "(?://[^\\n\\r]*$)";
	private static final String BLOCK_COMMENT        = "(?:/\\*[^\\*]*\\*(?:[^/][^\\*]*\\*)*/)";
	private static final String COMMENT_OR_WHITSPACE = "(?:"+LINE_COMMENT+"|"+BLOCK_COMMENT+"|\\s)";
	private static final String SEPARATOR            = COMMENT_OR_WHITSPACE+"+";
	private static final String OPTIONAL_SEPARATOR   = COMMENT_OR_WHITSPACE+"*";

	// TODO: Allow for generics in interface definition
	private static final String IDENTIFIER           = "[\\p{L}_$][\\p{L}\\p{N}_$]*";
	private static final String PACKAGE_NAME         = "(?:"+IDENTIFIER+"(?:\\."+IDENTIFIER+")*)";
	private static final String PACKAGE_LIST         = "(?:"+PACKAGE_NAME+OPTIONAL_SEPARATOR+","+OPTIONAL_SEPARATOR+")*"+PACKAGE_NAME;
	
	private static final String PUBLIC_OR_ABSTRACT   = "(?:(?:public"+SEPARATOR+")|(?:abstract"+SEPARATOR+"))*";

	private static final String PACKAGE       		 = OPTIONAL_SEPARATOR+"package"+SEPARATOR+"("+PACKAGE_NAME+")"+OPTIONAL_SEPARATOR+";";
	private static final String IMPORT       		 = OPTIONAL_SEPARATOR+"import" +SEPARATOR+PACKAGE_NAME+OPTIONAL_SEPARATOR+";";
	private static final String INTERFACE_OPENING    = OPTIONAL_SEPARATOR+PUBLIC_OR_ABSTRACT+"interface"+SEPARATOR+"("+IDENTIFIER+")"+SEPARATOR+"extends"+SEPARATOR+PACKAGE_LIST+OPTIONAL_SEPARATOR+"\\{";
	private static final String HEADER_PATTERN		 = "("+PACKAGE+"(?:"+IMPORT+")*"+INTERFACE_OPENING+")";
	private static final String FOOTER_PATTERN       = "("+OPTIONAL_SEPARATOR+"\\}"+OPTIONAL_SEPARATOR+")";
	private static final String FILE_PATTERN         = HEADER_PATTERN+"(.*?)"+FOOTER_PATTERN;
	
	private static final String SAFEHTML_OR_STRING   = "(?:SafeHtml)|(?:String)";
	// TODO: Associate end-of-line line-comments with previous statement
	// TODO: Allow for parametrized messages
	private static final String MESSAGE_PATTERN      = "\\G("+OPTIONAL_SEPARATOR+PUBLIC_OR_ABSTRACT+")("+SAFEHTML_OR_STRING+")("+SEPARATOR+")("+IDENTIFIER+")("+OPTIONAL_SEPARATOR+"\\("+OPTIONAL_SEPARATOR+"\\)"+OPTIONAL_SEPARATOR+";)|([^;]*;)";
	
	private static final Pattern FILE_PARSER = Pattern.compile(FILE_PATTERN,    Pattern.DOTALL + Pattern.MULTILINE);
	private static final Pattern BODY_PARSER = Pattern.compile(MESSAGE_PATTERN, Pattern.DOTALL + Pattern.MULTILINE);

	public static MessageFile parseFile(Directory directory, File file) {
		try {
			// Read entire file into string
			FileInputStream stream = new FileInputStream(file);
			byte[] content = new byte[stream.available()];
			stream.read(content, 0, content.length);
			stream.close();
			String text = new String(content);
			
			// Check if file imports Messages
			if ((!text.contains("com.google.gwt.i18n.client.Messages")) &&
				(!text.contains("com.google.gwt.i18n.client.Constants"))) return null;
			
			// Regex-match entire file to extract file body
			Matcher matcher = FILE_PARSER.matcher(text);
			if ((!matcher.matches()) || (matcher.groupCount()!=5)) return null;
			String fileHeader = matcher.group(1);
			String pkgname    = matcher.group(2);
			String identifier = matcher.group(3);
			String fileBody   = matcher.group(4);
			String fileFooter = matcher.group(5);
			
			MessageFile result = new MessageFile(directory, pkgname, identifier, fileHeader, fileFooter);
			
			// Regex-match file body to extract message definitions
			matcher = BODY_PARSER.matcher(fileBody);
			int lastEnd = 0;
			while (matcher.find()) {
				if (matcher.groupCount()!=6) return null;
				if (matcher.group(6)==null) {
					String      prefix = matcher.group(1);
					MessageType type   = ("SafeHtml".equals(matcher.group(2))) ? MessageType.SAFEHTML : MessageType.STRING;
					String      infix  = matcher.group(3);
					String      name   = matcher.group(4);
					String      suffix = matcher.group(5);
					result.addMessage(name, type, prefix, infix, suffix);
				} else {
					result.addLiteral(matcher.group(6));
				}
				lastEnd = matcher.end();
			}
			
			// Make sure entire file body was matched so that we won't loose
			// data if we rebuild the file later
			if (lastEnd!=fileBody.length()) return null;
			
			return result;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
}
